package com.heima.Unsafe;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @Author: Ethan Yankang
 * @Program: 面试必会代码
 * @Date: 2024-09-03 11:04
 **/
public class UnsafeExam implements Runnable{

    boolean flag=false;
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("子线程将flag状态改变为："+flag);
        flag=true;
    }

    //通过反射获取Unsafe类中已经实例化完成的theUnsafe对象
    public static Unsafe reflectGetUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (NoSuchFieldException e) {//处理异常
            e.printStackTrace();
            return null;//返回内容
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        UnsafeExam aUnsafeExam = new UnsafeExam();
        new Thread(aUnsafeExam).start();
        Unsafe unsafe= UnsafeExam.reflectGetUnsafe();
        while(true){
            //加入读内存屏障后，能立即识别到子线程的flag变化
            /*
             * @description 如果没有loadFence方法 那么主线程没有办法感知到flag的变化，因为在Java内存模型中，运行的线程不是直接读取主存中的变量，
             * 而是修改线程本身的工作内存的变量，然后传给主内存的，并且线程和线程之间的内存是独立的，如果主线程要感知flag变化，那么就应该借助于主内存，
             * 主内存将修改后的flag同步给主线程，因为loadFence屏障之前的线程执行结束了，将缓存数据设置为无效，然后重新读取最新的主存内容，
             * 只有在合适的地方加入屏障，才可以保证内存可见性。
             * @params
             * @return
             */
            unsafe.loadFence();
            if (aUnsafeExam.flag){
                System.out.println("检测到flag改变了,flag= "+ aUnsafeExam.flag);
                break;
            }
        }
        System.out.println("main Thread end");
    }
}
